/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.wider.foundation.channel;

import com.wider.foundation.TimeLimited;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * This class implements the ChannelRequestIntf interface that abstracts the standard
 * form-factor of a Channel Request. This class implements the Iterable interface so
 * that user can iterate the request parameters directly.
 *
 * Iterator is provided for iterating over the parameters.
 *
 * @author William Choi
 */
public class ChannelRequest extends TimeLimited implements ChannelRequestIntf{
    private ChannelResponseIntf   req_handlr;
    private Queue<Queue>    waitqueues;
    private Map<?, ?>       req_params;
    private String          req_method;
    private Object          req_clbkid;
    private boolean         req_cncled;
    private boolean         req_cmpled;
    private String          req_rspmsg;
    private InputStream     req_strmin;
    private int             req_rtcode;
    private int             timeoutval;

    /**
     * Default Constructor for constructing an Request instance from the parameters, handler, callback ID and timeout value.
     *
     * @param params        Request parameters carried by a Map
     * @param handler       NetChannelResponseHandler implementation
     * @param callbackID    An object reference which acts as a callback ID for handler's use
     * @param timeout       Timeout value of the request
     */
    public ChannelRequest(String method, Map<?, ?> params, ChannelResponseIntf handler, Object callbackID, int timeout){
        super(timeout);

        waitqueues  = new ConcurrentLinkedQueue<Queue>();
        req_method  = method;
        req_params  = params;
        req_handlr  = handler;
        req_clbkid  = callbackID;
        req_cncled  = false;
        req_cmpled  = false;
        req_strmin  = null;
        req_rspmsg  = null;
        req_rtcode  = 0;
        timeoutval  = timeout;
    }

    /**
     * Public method for figuring out which method that the user desires to use for this request
     *
     * @return The request method string
     */
    public String getMethod(){
        return req_method;
    }

    /**
     * Public method for retrieving the timeout value set for the request
     * @return  The timeout value in milli-second
     */
    public int getTimeoutValue(){
        return timeoutval;
    }

    /**
     * Public method for calling back to the response handler of the request.
     * @param resp_code
     * @param resp_mesg
     * @param stream_in
     * @throws IOException
     */
    public void callback(int resp_code, String resp_mesg, InputStream stream_in) throws IOException{
        req_rspmsg = resp_mesg;
        req_rtcode = resp_code;
        req_strmin = stream_in;

        synchronized(this){
            req_cmpled = true;
            this.notifyAll();
        }

        if (null != req_handlr){
            req_handlr.handle(req_clbkid, resp_code, resp_mesg, stream_in);
        }
    }

    /**
     * Public method for calling back to the response handler of the request with Exception
     * @param ex
     */
    public void callback(Throwable ex){
        req_rspmsg = ex.getMessage();
        req_rtcode = -1;
        req_strmin = null;
        
        synchronized(this){
            req_cmpled = true;
            this.notifyAll();
        }
        
        if (null != req_handlr){
            req_handlr.handle(req_clbkid, ex);
        }
    }
    
    /**
     * Interface Iterable standard method for getting the Iterator for iterating
     * the parameter key-value map
     *
     * @return  The Iterator for iterating the parameter key-value map
     */
    @Override
    public Iterator iterator() {
        return req_params.entrySet().iterator();
    }

    @Override
    public boolean isCompleted() {
        return req_cmpled;
    }

    @Override
    public int getResponseCode() {
        return req_rtcode;
    }

    @Override
    public String getResponseMessage() {
        return req_rspmsg;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return req_strmin;
    }

    @Override
    public boolean cancel() {
        if (req_cmpled){
            return false;
        }
        if (req_cncled){
            return true;
        }
        
        leaveAllQueues();
        synchronized(this){
            req_cncled = true;
            this.notifyAll();
        }
        return true;
    }

    @Override
    public boolean isCancelled() {
        return req_cncled;
    }

    @Override
    public void join() throws InterruptedException {
        synchronized(this){
            while((!req_cmpled) && (!req_cncled)){
                this.wait(timeoutval);
            }
        }
    }

    @Override
    public void enterQueue(Queue q) {
        waitqueues.add(q);
    }

    @Override
    public boolean exitQueue(Queue q) {
        waitqueues.remove(q);
        return true;
    }

    @Override
    public boolean leaveQueue(Queue q) {
        boolean ret = false;
        ret = q.remove(this);
        exitQueue(q);

        return ret;
    }

    @Override
    public boolean isQueued() {
        return (!waitqueues.isEmpty());
    }

    @Override
    public long leaveAllQueues() {
        long ret = 0;

        if (isQueued()){
            Queue q;
            while (null != (q = waitqueues.peek())){
                leaveQueue(q);
                ret++;
            }
        }
        return ret;
    }

    @Override
    public int getParamCount() {
        return req_params.size();
    }

}
